Acquisition de signal avec une carte son


Lorsque que l'on veut acquérir et numériser des signaux, une solution simple consiste à utiliser la carte son présente dans pratiquement tous les ordinateurs. La seule limitation de cette solution est qu'il faut que ces signaux ne soient pas continus (ou très très basse fréquence). En effet toutes les cartes ont un filtre passe-haut en entrée qui élimine ces basses fréquences.

Pour accéder à la carte, j'ai utilisé la bibliothèque Qt qui possède dans son module multimedia un ensemble riche de fonctionnalités son et vidéo, et pour ce qui nous intéresse la possibilité d'accéder directement aux échantillons numérisés en entrée.

Pour commencer, après avoir créé votre projet Qt (graphique ou non), vous devez spécifier l'utilisation du module multimedia dans celui ci:

#-------------------------------------------------
#
# Project created by QtCreator 2013-01-29T15:02:11
#
#-------------------------------------------------

QT       += core gui multimedia

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = sound-acquire
TEMPLATE = app

SOURCES += main.cpp\
       widget.cpp
HEADERS  += widget.h
FORMS    += widget.ui

L'accés au son en entrée se fait par l'intermédiaire de quatre classes:

  • QAudioInput, qui fournit une interface pour recevoir des données audio à partir d'un périphérique d'entrée audio.
  • QAudioFormat, qui représente les paramètres du flux audio
  • QAudioDeviceInfo, qui fournit une interface pour interroger les périphériques audio et leur fonctionnalité.
  • QIODevice, qui est la classe représentant tous les périphériques d'entrée/sortie sous Qt.

Il suffit donc, dans un premier temps, d'instancier ces classes (créer des objets de ces classes), et de créer un buffer pour recvoir les échantillons. Ceci peut être fait dans une classe principale (d'interface par exemple) ou directement dans le main.

QAudioInput *audioInput;        // to get the sound
QAudioFormat format;            // the audio format
QAudioDeviceInfo info;          // info on the sound board
QIODevice *inputDevice;

// le buffer de réception
uchar *dataSound;

Le fichier de la classe principale, widget.h pour moi, doit ressembler à:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QtMultimedia>
#include <QAudioFormat>
#include <QAudioInput>
#include <QAudioOutput>
#include <QSound>
#include <QIODevice>
using namespace std;

#define SOUND_BUFFER_SIZE 0xffff

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();


private slots:
    void on_toolButton_Record_Stop_clicked();
    void data2Read();
    void stateChanged(QAudio::State state);


private:
    Ui::Widget *ui;

    // to acquire the sound
    QAudioInput *audioInput;        // to get the sound
    QAudioFormat format;            // the audio format
    QAudioDeviceInfo info;          // info on the sound board
    QIODevice *inputDevice;

    // the receive buffer
    uchar *dataSound;
    };
#endif // WIDGET_H

Dans le fichier d'implémentation, widget.cpp pour moi, nous allons maintenant vérifier la présence d'une ou plusieurs cartes son sur notre système:

QList<QAudioDeviceInfo> availableDevices = QAudioDeviceInfo::availableDevices ( QAudio::AudioInput );

La liste availableDevices fournit toutes les informations sur les cartes présentes. Pour les représenter sur l'interface graphique, je les mets leur nom dans un QComboBox:

for (int i=0 ; i<availableDevices.size() ; i++)
        ui->comboBoxDevice->addItem(availableDevices[i].deviceName());

puis je sélectionne la carte son par défaut:

// si au moins une carte présente
if (availableDevices.size()){
        info = QAudioDeviceInfo::defaultInputDevice();
        }

Enfin, j'alloue de l'espace mémoire pour le buffer d'entrée:

// le buffer d'acquisition sur la carte son
dataSound = new unsigned char [SOUND_BUFFER_SIZE];

Pour acquérir du son, ou autre chose, il faut dans un premier temps choisir les paramètres du flux - fréquence d'échantillonage, nombre de voies, la profondeur des échantillons et l'endian:

//     get the format from UI
if (!info.isFormatSupported(format)) {
   format = info.nearestFormat(format);
   }

// pour notre appli:
format.setSampleRate(8000);                        // echantillons par seconde
format.setChannelCount(1);                          // mono, une seule voie
format.setSampleSize(16);                           // deux octets par échantillon
format.setByteOrder(QAudioFormat::LittleEndian);

instancier un canal d'entrée avec ce format:

// audio input and inputDevice
if (audioInput)delete audioInput;                // le canal son de la carte
audioInput = new QAudioInput(format, this);      // création de l'entrée audio
audioInput->setBufferSize(SOUND_BUFFER_SIZE);          // taille du buffer
audioInput->setNotifyInterval(1);
audioInput->setVolume(1);

et enfin lancer l'acquisition à proprement parler:

// lancement de l'acquisition
connect(inputDevice, SIGNAL(readyRead()), this, SLOT(data2Read()));
inputDevice = audioInput->start();
connect(inputDevice, SIGNAL(readyRead()), this, SLOT(data2Read()));

En connectant le signal readyRead (des échantillons sont prêts à être lue) de inputDevice à une fonction (un slot), ici data2Read, cette dernière sera automatiquement appelée à chaque fois que des échantillons seront présent. Il ne reste plus qu'à les récupérer pour les utiliser comme bon vous semble:

// lecture des échantillons dans le buffer
    int count = inputDevice->read((char *)dataSound, SOUND_BUFFER_SIZE);
//     si multivoies, la structure du buffer est
//    data[] : [Octets du Sample 1 du Canal 1] [Octets du Sample 1 du Canal 2]
//             [Octets du Sample 2 du Canal 1] [Octets du Sample 2 du Canal 2] ...

// on récupère des entiers sur 16 bits avec un pointeur
u_int16_t *sample = (u_int16_t *)dataSound;
// on affiche, à vous d'en faire autre chose!
prinft("%d bytes read:", count);
for (int i=0 ; i<count/2 ; i++)printf("%04d ", sample[i]);
printf("\n");

Vous pouvez récupérer le projet Qt complet ici.

A vous l'acquisition de signaux mono ou stéréo, sur 8, 16 ou 32 bits, de 8 à 192 kHz, très simplement et sans débourser un centime ...